#==================================================
#	Gene Texture
#==================================================
#  生成 Texture 数据
#==================================================
# @datetime: 2022-1-25 16:47:47
#==================================================
tool
extends MarginContainer


const ItemScn = preload("res://addons/animated_sprite_frame_tool/scene/Item.tscn")
const Item = preload("res://addons/animated_sprite_frame_tool/scene/Item.gd")


var editor_interface : EditorInterface setget set_editor_interface

var directory = Directory.new()	# 操作目录
var regex = RegEx.new()	# 正则匹配

var dir_path = "" setget set_dir_path	# 当前扫描的路径
var config = {}		# 配置数据
var texture_file_list = []	# 扫描目录中的图片文件名列表
var current_item : Item		# 当前选中的动画项 Item
var selected_anim_player : AnimationPlayer	# 选中的AnimationPlayer节点 


# ========================= [ 弹窗 ]
onready var scan_dir_dialog = $Dialog/ScanDir
onready var save_sprite_frames_dialog = $Dialog/SaveSpriteFramesFile
onready var load_json_data_dialog = $Dialog/LoadJsonData
onready var save_comp_sprite_dialog = $Dialog/SaveCompSprite

# ========================= [ 动画项 ] 
onready var name_format_edit = find_node("NameFormat")
onready var path_label = find_node("Path")
onready var item_container = find_node("ItemContainer")
onready var item_count_label = find_node("ItemCount")

# ========================= [ 播放动画 ]
onready var anim_sprite = find_node("AnimatedSprite")
onready var frame_slider = find_node("FrameSlider")
onready var playing_anim_label = find_node("PlayingAnim")
onready var add_to_anim_player = find_node("AddToAnimPlayer")
onready var all_add_to_anim_player = find_node("AllAddToAnimPlayer")
onready var anim_sprite_node_path = find_node("AnimSpriteNodePath")

# ========================= [ 多张图合成一张 ]
onready var composition_sprite = find_node("CompositionSprite")
onready var comp_x = find_node("Comp_X")
onready var comp_y = find_node("Comp_Y")



#==================================================
# Set/Get
#==================================================
func set_editor_interface(value: EditorInterface) -> void:
	editor_interface = value
	editor_interface.get_selection().connect("selection_changed", self, "_on_node_selection_changed")


func get_editor_interface() -> EditorInterface:
	return editor_interface


func set_dir_path(value):
	dir_path = value
	path_label.text = dir_path
	path_label.hint_tooltip = "扫描路径：" + dir_path
	config["dir_path"] = dir_path
	scan_dir_dialog.current_dir = dir_path
	texture_file_list = get_file_list()


func get_config_path() -> String:
#	return filename.get_base_dir().plus_file(".config.cuscfg")
	return ".config.cuscfg"


##  获取文件列表
## @return  返回文件夹下所有的文件
func get_file_list() -> Array:
	if (dir_path == ""
		or directory.open(dir_path) != OK
	):
		return []
	# 获取文件列表
	directory.list_dir_begin()
	var texture_file_list = []
	var file = directory.get_next()
	while file != "":
		if not directory.current_is_dir():
			if file.get_extension() in [
				'png', 'jpg', "jpeg", 'bmp', 
				"atlastex", "curvetex", "dds", "exr", "hdr", 
				"largetex", "meshtex", "pkm", "pvr", "res", "svg", 
				"svgz", "tex", "tag", "tres", "webp"
			]:
				texture_file_list.push_back(dir_path.plus_file(file))
		file = directory.get_next()
	return texture_file_list


##  获取 Item 列表中的数据 
## @return  
func get_item_data_list() -> Array:
	var list = []
	if item_container:
		for child in item_container.get_children():
			if child is Item:
				list.push_back(child.get_data())
	return list



#==================================================
#   内置方法
#==================================================
func _ready():
	self.connect("tree_exiting", self, "save_config")
	set_dir_path("")
	load_config()
	
	# 对选中的节点做判断
	_on_node_selection_changed()


func _physics_process(delta):
	if anim_sprite:
		var texture = anim_sprite.texture as AnimatedTexture
		if texture && frame_slider:
			frame_slider.value = texture.current_frame



#==================================================
#   自定义信号
#==================================================
# 加载配置文件
func load_config():
	var config_path = get_config_path()
	var file = File.new()
	if file.file_exists(config_path):
		if file.open(config_path, File.READ) == OK:
			config = parse_json(file.get_as_text())
			file.close()
		if config.has("dir_path"):
			set_dir_path(config['dir_path'])
		# Item 列表
		if config.has("item_data_list"):
			load_item_data_list(config.item_data_list)
		set_object_prop_by_config("name_format", name_format_edit, "text")
		set_object_prop_by_config("comp_x", comp_x, "text")
		set_object_prop_by_config("comp_y", comp_y, "text")


##  根据 Config 配置设置对象的属性
func set_object_prop_by_config(key, node: Object, prop: String):
	if config.has(key):
		node.set(prop, config[key])


##  保存配置
func save_config():
	config['item_data_list'] = get_item_data_list()
	config['name_format'] = name_format_edit.text
	config['comp_x'] = comp_x.text
	config['comp_y'] = comp_y.text
	var file = File.new()
	if file.open(get_config_path(), File.WRITE) == OK:
		file.store_string(to_json(config))
		print("保存配置完成: ", self.filename.get_base_dir().plus_file(file.get_path()))
		file.close()


##  加载 Item 数据列表 
## @item_data_list  item数据列表
## @append  是否追加到节点上
func load_item_data_list(item_data_list: Array, append: bool = false) -> void:
	if not append:
		clear_item_list()
	for item_data in item_data_list:
		add_item(item_data)
	if item_data_list.size() == 0:
		item_container.add_child(ItemScn.instance())


##  添加 Item 
## @item_data  Item中的数据
func add_item(item_data: Dictionary = {}) -> Item:
	var item = ItemScn.instance() as Item
	item_container.add_child(item)
	if item_data:
		item.set_data(item_data)
	item.connect("focus_entered", self, "_on_Item_focus_entered", [item])
	item.connect("changed", self, "_on_Item_changed", [item])
	item.connect("tree_exited", self, "update_item_count")	# item 删除后更新数量
	update_item_count()
	return item


##  搜索 texture 文件列表
## @start  起始idx
## @end  结束idx
## @return  
func find_texture_file_list(start: int, end: int) -> Array:
	var list = []
	for i in range(start, end+1):
		var file = find_texture_file(i)
		if file:
			list.push_back(file)
	return list


##  搜索 texture 文件
## @idx  文件的idx索引值（文件序号值）
## @return  返回 texture 文件列表
func find_texture_file(idx: int) -> String:
	var pattern = name_format_edit.text.replace("{{idx}}", idx)
	regex.compile(pattern)
	var file := ""
	for i in range(texture_file_list.size() - 1, -1, -1):
		file = texture_file_list[i]
		if regex.search(file.get_file()) != null:
			return file
			break
	return ""


##  清除Item列表
func clear_item_list() -> void:
	for item in item_container.get_children():
		if item is Item:
			item.queue_free()
	update_item_count()


##  对 AnimatedSprite 进行播放动画
## @data  item数据
func play_texture(data: Dictionary) -> void:
	var anim_texture := AnimatedTexture.new()
	var texture_file_list = find_texture_file_list(data.start, data.end)
	if texture_file_list.size() > 0:
		anim_texture.frames = texture_file_list.size()
		for idx in texture_file_list.size():
			var texture = load(texture_file_list[idx]) as Texture
			anim_texture.set_frame_texture(idx, texture)
		anim_texture.oneshot = not data.loop
		anim_texture.fps = data.speed
		anim_sprite.texture = anim_texture
		# 设置滑动条最大帧数
		frame_slider.value = 0
		frame_slider.max_value = texture_file_list.size() - 1
		# 显示标签动画名称
		playing_anim_label.text = "(%s)" % data.name


##  下一帧
## @count  跳转增加的帧数（可以是负数）
func next_frame(count: int):
	var texture := anim_sprite.texture as AnimatedTexture
	if texture:
		texture.pause = true
		# 下一帧
		if (count > 0 
			and texture.current_frame + count < texture.frames
		):
			texture.current_frame += count
		
		# 上一帧
		elif (count < 0 
			and texture.current_frame + count >= 0
		):
			texture.current_frame += count
		



#==================================================
#   生成文件
#==================================================
##  生成 Sprite Frame 文件
func generate_sprite_frame() -> SpriteFrames:
	if texture_file_list.size() == 0:
		return null
	
	var item_list = get_item_data_list()
	# 添加动画
	var frames = SpriteFrames.new()
	for item in item_list:
		add_animation_to_sprite_frames(item, frames)
	return frames


##  添加动画到 Sprite Frames
## @item_data  item 中的 Key 需要是下面的几个
## @sprite_frames  要添加到的 SpriteFrames
func add_animation_to_sprite_frames(item_data: Dictionary, sprite_frames: SpriteFrames):
	var anim_name = item_data.name
	var start = item_data.start
	var end = item_data.end
	# 添加动画
	sprite_frames.add_animation(anim_name)
	# 搜索对应的图片文件
	var texture_file_list = find_texture_file_list(start, end)
	# 设置动画属性
	for file in texture_file_list:
		sprite_frames.add_frame(anim_name, load(file))
	sprite_frames.set_animation_loop(anim_name, item_data.loop)
	sprite_frames.set_animation_speed(anim_name, item_data.speed)


##  生成合成后的 Texture 显示到 Sprite 上的效果
func generate_composition_sprite():
	var size = Vector2(0,0)
	if comp_x.text != "" and comp_y.text != "":
		size.x = int(comp_x.text)
		size.y = int(comp_y.text)
	
	var item_list = get_item_data_list()
	if item_list.size() == 0:
		print("[ERROR] 没有扫描到文件，生成失败")
		return
	
	var start = -1
	var end = -1
	var images_list = []
	var new_image_size = Vector2(0,0)
	var temp_image : Image
	for item in item_list:
		start = item.start
		end = item.end
		# 添加 image
		var images = []
		var max_width = 0
		var max_height = 0
		var texture_file_list = find_texture_file_list(start, end)
		var texture
		var image
		for file in texture_file_list:
			texture = load(file) as Texture
			image = texture.get_data()
			if size != Vector2(0,0):
				image.resize(size.x, size.y)
			images.push_back(image)
			# 设置数据
			max_width += image.get_size().x
			if max_height < image.get_size().y:
				max_height = image.get_size().y
			temp_image = image
		if new_image_size.x < max_width:
			new_image_size.x = max_width
		new_image_size.y += max_height
		images_list.push_back(images)
	
	if images_list.size() == 0:
		return
	
	# 创建新的 Image
	var new_image = Image.new()
	new_image.create(new_image_size.x, new_image_size.y, true, temp_image.get_format())
	
	var pos = Vector2(0,0)
	for images in images_list:
		pos.x = 0
		var y = 0
		for image in images:
			image = image as Image
			var img_size = image.get_size() as Vector2
			new_image.blit_rect(image, Rect2(Vector2(0,0), img_size), pos)
			pos.x += img_size.x
			if y < img_size.y:
				y = img_size.y
		pos.y += y
	
	# 显示到节点上
	var texture = ImageTexture.new()
	texture.create_from_image(new_image)
	composition_sprite.texture = texture


##  更新 Item 标签数量个数 
func update_item_count():
	if item_container:
		item_count_label.text = str(item_container.get_child_count())


##  添加动画到动画播放器中
func add_anim_to_player(
	anim_name: String, 
	animated_sprite_path: String,
	animated_sprite: AnimatedSprite, 
	animation_player: AnimationPlayer
):
	if anim_name == "":
		printerr("动画名不能为空")
		return
	
	if animated_sprite.frames == null:
		# 没有设置这个属性，则自动添加一个
		animated_sprite.frames = SpriteFrames.new()
	
	var sprite_frames := animated_sprite.frames as SpriteFrames
	
	# 判断是否有这个动画，如果没有则进行添加
	if not sprite_frames.has_animation(anim_name):
		var item_list = get_item_data_list()
		for item in item_list:
			if item.name == anim_name:
				# 添加这个动画
				add_animation_to_sprite_frames(item, sprite_frames)
	
	# 开始添加动画到 AnimationPlayer
	if sprite_frames:
		# 生成动画
		var animation = Animation.new()
		var count = sprite_frames.get_frame_count(anim_name)
		var speed = 1.0 / sprite_frames.get_animation_speed(anim_name)
		
		# 设置基本属性
		animation.length = speed * count
		animation.loop = sprite_frames.get_animation_loop(anim_name)
		# 要播放的动画名时间线
		var anim_track = animation.add_track(Animation.TYPE_VALUE)
		animation.track_set_path(anim_track, animated_sprite_path + ":animation")
		animation.track_insert_key(anim_track, 0.0, anim_name)
		# 设置 AnimatedSprite 的 playing 为 false
		var playing_track = animation.add_track(Animation.TYPE_VALUE)
		animation.track_set_path(playing_track, animated_sprite_path + ":playing")
		animation.track_insert_key(playing_track, 0.0, false)
		# 播放的对应的帧时间线
		var frame_track = animation.add_track(Animation.TYPE_VALUE)
		animation.track_set_path(frame_track, animated_sprite_path + ":frame")
		for i in count:
			animation.track_insert_key(frame_track, i * speed, i)
		
		# 添加到动画播放器中
		selected_anim_player.add_animation(anim_name, animation)
		print(anim_name, "动画添加完成")



#============================================================
#   连接信号
#============================================================

# ==================== [ 按钮 ] 
func _on_Add_pressed():
	# 添加一个动画 Item
	var item_data = {}
	if item_container.get_child_count() > 0:
		# 设置这个 item 的 start 属性
		var last_item = item_container.get_child(item_container.get_child_count() - 1) as Item
		item_data = {start = last_item.get_data().end + 1}
	add_item(item_data)


func _on_Clear_pressed():
	# 清空 Item 列表
	clear_item_list()


func _on_Generate_pressed():
	# 保存文件
	save_sprite_frames_dialog.popup_centered()


func _on_LoadData_pressed():
	# 加载数据文件
	load_json_data_dialog.popup_centered()


func _on_LoadDir_pressed():
	# 加载目录
	scan_dir_dialog.popup_centered()


func _on_PlayAnim_pressed():
	# 播放动画
	var texture := anim_sprite.texture as AnimatedTexture
	if texture:
		texture.pause = true
		texture.current_frame = 0
		texture.pause = false


func _on_StopAnim_pressed():
	# 停止播放动画
	var texture := anim_sprite.texture as AnimatedTexture
	if texture:
		texture.pause = true
		texture.current_frame = 0


func _on_SaveSpriteFrame_pressed():
	# 显示保存 SpriteFrames 弹窗
	if composition_sprite.texture == null:
		print("CompositionSprite 没有 texture 数据")
		return
	save_comp_sprite_dialog.popup_centered()


##  检测添加动画到 AnimationPlayer 上的条件
## @return  
func detect_add_anim_player() -> bool:
	var root = get_editor_interface().get_edited_scene_root()
	if not root.has_node(anim_sprite_node_path.text):
		printerr("没有这个节点，节点路径错误")
		return false
	var am_sp := root.get_node(anim_sprite_node_path.text) as AnimatedSprite
	if am_sp == null:
		printerr("节点类型错误，必须是 AnimatedSprite 类型的节点")
		return false
	if not is_instance_valid(selected_anim_player):
		printerr("选中的节点无效，请重新选定 AnimationPlayer")
		return false
	return true


func _on_AddToAnimPlayer_pressed():
	if detect_add_anim_player():
		if current_item == null:
			printerr("当前没有播放动画！")
			return
		var root = get_editor_interface().get_edited_scene_root()
		var anim_name := current_item.get_anim_name() as String
		var animated_sprite = root.get_node(anim_sprite_node_path.text)
		add_anim_to_player(
			anim_name, anim_sprite_node_path.text, 
			animated_sprite, selected_anim_player
		)
		# 取消选中，重新选中
		get_editor_interface().get_selection().clear()
		get_editor_interface().edit_node(selected_anim_player)


func _on_AllAddToAnimPlayer_pressed():
	if detect_add_anim_player():
		var root = get_editor_interface().get_edited_scene_root()
		var animated_sprite = root.get_node(anim_sprite_node_path.text) as AnimatedSprite
		var sprite_frame = animated_sprite.frames as SpriteFrames
		var anim_names = sprite_frame.get_animation_names()
		for anim_name in anim_names:
			add_anim_to_player(
				anim_name, anim_sprite_node_path.text, 
				animated_sprite, selected_anim_player
			)
		# 取消选中，重新选中
		get_editor_interface().get_selection().clear()
		get_editor_interface().edit_node(selected_anim_player)



# ==================== [ 弹窗 ] 

func _on_SaveFile_file_selected(path: String):
	if path.get_file() == "":
		print("path 为空", path)
		return
	if path.is_valid_filename():
		print("文件名无效")
		return
	# 保存 SpriteFrames 文件
	var frames = generate_sprite_frame()
	if frames:
		print("保存文件：", path)
		if ResourceSaver.save(path, frames, ResourceSaver.FLAG_CHANGE_PATH) == OK:
			editor_interface.get_resource_filesystem().update_file(path)
			print("[ 保存完成 ]")
		else:
			printerr("保存失败！")


func _on_LoadDir_dir_selected(dir):
	# 设置读取图片帧的目录
	set_dir_path(dir)


func _on_LoadDataDialog_file_selected(path):
	# 加载 JSON 数据
	var item_data_list = []
	var file = File.new()
	if file.file_exists(path) and file.open(path, File.READ) == OK:
		var list = parse_json(file.get_as_text())
		if list is Array:
			load_item_data_list(list)
		else:
			printerr("数据必须是列表，JSON 数据必须是字典类型元素的列表！")


func _on_Item_focus_entered(item: Item):
	# Item 获得焦点，则开始进行播放动画
	if current_item == item:
		return
	# 播放动画
	current_item = item
	play_texture(item.get_data())


func _on_CompSpriteDialog_file_selected(path):
	# 保存图片
	if composition_sprite.texture == null:
		print("CompositionSprite 没有 texture 数据")
		var image = composition_sprite.texture.get_data()
		image.save_png(path)
		editor_interface.get_resource_filesystem().update_file(path)
		editor_interface.get_resource_filesystem().scan_sources()


# ==================== [ 其他 ] 

func _on_Item_changed(item: Item):
	# item 数据发生改变时进行更新播放动画
	if item == current_item:
		current_item = null
		_on_Item_focus_entered(item)


#  滚动条值发生改变
func _on_FrameSlider_value_changed(value):
	if anim_sprite:
		var texture = anim_sprite.texture as AnimatedTexture
		# 做判断 physics_process 中修改滚动条后
		# 在这里防止再次更改，造成暂停问题
		if texture.current_frame != value:
			# 滑动条设置帧数，显示不同的帧数
			texture.pause = true
			texture.current_frame = value


##  选中的节点发生改变
func _on_node_selection_changed():
	selected_anim_player = null
	add_to_anim_player.disabled = true
	all_add_to_anim_player.disabled = true
	
	var nodes = get_editor_interface().get_selection().get_selected_nodes()
	if nodes.size() > 0:
		var node = nodes[0]
		# 选中 AnimationPlayer 则添加动画按钮可用
		if node is AnimationPlayer:
			selected_anim_player = node
			add_to_anim_player.disabled = false
			all_add_to_anim_player.disabled = false


